package cn.suimg.common;


import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.concurrent.atomic.AtomicLong;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Encode (or Decode) Util
 *
 * @author suimg
 */
public class EncryptUtil {

    /**
     * Hex Dict
     */
    public static final char[] HEX_DIGITS = {
            '0', '1', '2', '3',
            '4', '5', '6', '7',
            '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f'
    };

    /**
     * 通用16进制字符串
     */
    public static final String[] HEX_CHAR_ARRAY = new String[] {
            "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" , "i" , "j" , "k" , "l" ,
            "m" , "n" , "o" , "p" , "q" , "r" , "s" , "t" , "u" , "v" , "w" , "x" ,
            "y" , "z" , "0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" ,
            "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" , "I" , "J" , "K" , "L" ,
            "M" , "N" , "O" , "P" , "Q" , "R" , "S" , "T" , "U" , "V" , "W" , "X" ,
            "Y" , "Z"
    };

    /**
     * Base64 Encoder
     */
    private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();

    /**
     * Base64 Decoder
     */
    private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();

    /**
     * RSA公钥
     */
    private static final String RSA_PUBLIC_KEY_BASE64 =
            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn6Uj3Kp3QAvY8Cb9O+hSG6/ovlDsacdrtFSKi9gQZoVzIxGdCUGb5K9x" +
            "II/DSZYfgsvLD4Ml1xYTNqPdkh2Pybn6IdPTJMwSa1YkmEwO2DH1QChqSEqHTKU82Wf9WbMzQvgToZt6/XdFuHHozJldFTjzvjDB" +
            "yF2gJU1Iwg9Zah7JGgX/k9xUYXOWbQBA216Im60zNdfVqa0kuNIzbuxcGtlS4onV2ZRY3tOjSXPW+eZ2ymVRu1zgTer/OD6cUkk+" +
            "E4wjD3jQUaG94Nccb0y4EMZjvbD4fyLYEqxWCwOUL+l/5wiJsRd1r+3Gs4Mn21Cepl1m/AgpGXOD0BzTVHyTtwIDAQAB";

    /**
     * RSA私钥
     */
    private static final String RSA_PRIVATE_KEY_BASE64 =
            "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfpSPcqndAC9jwJv076FIbr+i+UOxpx2u0VIqL2BBmhXMjEZ0J" +
            "QZvkr3Egj8NJlh+Cy8sPgyXXFhM2o92SHY/Jufoh09MkzBJrViSYTA7YMfVAKGpISodMpTzZZ/1ZszNC+BOhm3r9d0W4cejMmV0V" +
            "OPO+MMHIXaAlTUjCD1lqHskaBf+T3FRhc5ZtAEDbXoibrTM119WprSS40jNu7Fwa2VLiidXZlFje06NJc9b55nbKZVG7XOBN6v84" +
            "PpxSST4TjCMPeNBRob3g1xxvTLgQxmO9sPh/ItgSrFYLA5Qv6X/nCImxF3Wv7cazgyfbUJ6mXWb8CCkZc4PQHNNUfJO3AgMBAAEC" +
            "ggEAB8GOLHpbsNDiiUhbpT10P8QcGqLz2W/G28v/ryugIFr7bQi+CGUXndSoOlzBsCGLBl/ATdChVgHMjSsI6MBm6/YNIPeZmoAP" +
            "pQdaX8vSLM7Ai41rHHgCesbMXgOfW2xJYUeMLJ3RK99Pk9Z06PvKee16ahMUVxe1YBqRHlyAi5c/lXU81bFR2BK/9DrW6nWyp4kU" +
            "1h9qWzlGZXAVc+Marw0cpsr+N1W/HFAH4FM3AN3mToJKfRIGYky7QpsuXkNdtsRKzni2JJAcLLsTY5OUwR3E08Z1NnsoMhRit4nS" +
            "Lh63lje8I3/+eqhE6LplwqKajG4bvvZ+Y/omnpgH7WvagQKBgQDc4gn3vTIQkbqEEmGgo3nf3n7GyOygKi398N2RlbqWL4a0/Sk0" +
            "VTWaY+9yJK7Z7bGtykONdL8OG1Jmmwg+Tjje4BmbYfawGpmWenlOai0xsTlbu40K3HhU6odf1AImzsF5BKEm8dBIKyumyd1w+cFx" +
            "AhSqBNEwwYAEhMNDKMy4bQKBgQC5BrZhAo1bf5hD9rJQCJFF5/n4tedPqF7cRYFDnp3yxWOvl5Ovewvk6E6hCNzXabi1/id5ttEA" +
            "KVcvLg+yKTslyIMgin3lQhtkHb5HDZFY+G73KRzGAIu+jgwiSAHuq7KAstxC+DEmOuObVHoY43WtE4H4YIWnvi3SNZbJnyluMwKB" +
            "gQCx3fu02U+FdDRDwiGHn9y2X46rOIUDfLPId7uUToQQCHlrKSmTEMK3bp4UxmFe8D9R+TxonohzLaEvKsx6j7bQEpnMPBjQOrtP" +
            "J5SjhnCaQWI08lRjWKI1jx4wFqMc6QV3FDlJCRR16P2fs5J0NUqGW+yT9IkXv9n8yYQLgsFICQKBgGjX9OBjodYHJzDbyY6wTCFg" +
            "obTc3oW00ovelrDKYJYAY8xMB9OX1dWQDX4Sl5E4xwHZUmUVwrhktJJ3d01tL6bpR+RYlZ4jJ4JDmF6KwaG4pKhJE5TfGlCQDTAf" +
            "vXiQskVbFJSeLruLAw4xgD46JaQ9OZiUmSfEDBLhauA04DUhAoGAJlhRNgOCZvXwVouV77mx9oVHsE9HdvnupBM/H1YmbJDQqyzv" +
            "Ro/q5DrVoKVyionX/9orPYkQfnyrOPYkDyTP7npPLePD2dx4XfH9DgNqqfF/j9W2WrvgCuyzosRIMJ2bI6/zYt5WWusZmyWX8ktY" +
            "xqi6B+RRXzvPCXTiw3dBJ14=";

    /**
     * 提前解码之后的RSA公钥
     */
    private static final byte[] RSA_PUBLIC_KEY = base64Decode2Bytes(RSA_PUBLIC_KEY_BASE64);

    /**
     * 提前解码之后的RSA私钥
     */
    private static final byte[] RSA_PRIVATE_KEY = base64Decode2Bytes(RSA_PRIVATE_KEY_BASE64);

    /**
     * Logger
     */
    private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);
    

    private static final int MAX_ENCRYPT_BLOCK = 117;

    private static final int MAX_DECRYPT_BLOCK = 128;

    /**
     * Sha1 Encode
     *
     * @param str
     * @return
     */
    public static String sha1(String str) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
            messageDigest.update(str.getBytes());
            byte[] bytes = messageDigest.digest();
            int len = bytes.length;
            StringBuilder buf = new StringBuilder(len * 2);
            for (int j = 0; j < len; j++) {
                buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
                buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
            }
            return buf.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Encode Base64 bytes -> bytes
     *
     * @param str
     * @return
     */
    public static String base64Encode(byte[] bytes) {
        return new String(BASE64_ENCODER.encode(bytes));
    }

    /**
     * Decode Base64 bytes -> bytes
     *
     * @param str
     * @return
     */
    public static byte[] base64Decode(byte[] bytes) {
        return BASE64_DECODER.decode(bytes);
    }

    /**
     * Encode Base64 String -> String
     *
     * @param bytes
     * @return
     */
    public static String base64Encode(String str) {
        return new String(base64Encode(str.getBytes()));
    }

    /**
     * Decode Base64 String -> String
     * @param str
     * @return
     */
    public static String base64Decode(String str) {
        return new String(base64Decode(str.getBytes()));
    }

    /**
     * Decode Base64 String -> bytes
     * @param str
     * @return
     */
    public static byte[] base64Decode2Bytes(String str){
        return base64Decode(str.getBytes());
    }


    /**
     * Encode MD5
     *
     * @param key
     * @return
     */
    public static String md5(byte[] bytes) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(bytes);
            byte[] digest = messageDigest.digest();
            int j = digest.length;
            char result[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = digest[i];
                result[k++] = HEX_DIGITS[byte0 >>> 4 & 0xf];
                result[k++] = HEX_DIGITS[byte0 & 0xf];
            }
            return new String(result);
        } catch (Exception e) {
            return null;
        }
    }

    public static String md5(String str){
        return md5(str.getBytes());
    }

    /**
     * Encode MD5 with slat
     *
     * @param key
     * @param slat
     * @return
     */
    public static String md5(String key, String slat) {
        return md5(slat + key + slat);
    }


    /**
     * AES Encrypt
     *
     * @param content
     * @param password
     * @return
     */
    public static byte[] aesEncrypt(byte[] content, byte[] password) {
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, getAesSecretKey(password));
            return cipher.doFinal(content);
        } catch (Exception e) {
            logger.error("Encrypt aes error:{}", e.getMessage());
        }
        return null;
    }

    /**
     * Aes Encrypt
     * @param content
     * @param password
     * @return
     */
    public static String aesEncrypt(String content,String password){
        return base64Encode(aesEncrypt(content.getBytes(),password.getBytes()));
    }

    /**
     * AES Decrypt
     *
     * @param content
     * @param password
     * @return
     */
    public static byte[] aesDecrypt(byte[] content, byte[] password) {
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, getAesSecretKey(password));
            return cipher.doFinal(content);
        } catch (Exception e) {
            logger.error("Decrypt aes error:{}", e.getMessage());
        }
        return null;
    }

    /**
     * AES Decrypt
     * @param content
     * @param password
     * @return
     */
    public static String aesDecrypt(String content,String password){
        return new String(aesDecrypt(content.getBytes(),password.getBytes()));
    }

    /**
     * 生成加密秘钥
     *
     * @return
     */
    private static SecretKeySpec getAesSecretKey(byte[] password) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(password);
            keyGenerator.init(128, random);
            SecretKey secretKey = keyGenerator.generateKey();
            return new SecretKeySpec(secretKey.getEncoded(), "AES");
        } catch (Exception e) {
            logger.error("getAesSecretKey error:{}", e.getMessage());
        }
        return null;
    }

    /**
     * RSA -> 对数据本体进行签名
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] rsaSignData(byte[] data) throws Exception {
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(RSA_PRIVATE_KEY);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(priKey);
        signature.update(data);
        return signature.sign();
    }

    /**
     * RSA -> 校验数据签名
     * @param data
     * @param sign
     * @return
     * @throws Exception
     */
    public static boolean rsaVerifyData(byte[] data,byte[] sign) throws Exception {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(RSA_PUBLIC_KEY);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(pubKey);
        signature.update(data);
        return signature.verify(sign);
    }

    /**
     * RSA 使用公钥进行加密
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] rsaEncryptByPublicKey(byte[] data) throws Exception {
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(RSA_PUBLIC_KEY);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key publicKey = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }


    /**
     * RSA -> 使用公钥进行解密
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] rsaDecryptByPublicKey(byte[] data) throws Exception {
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec (RSA_PUBLIC_KEY);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key publicKey = keyFactory.generatePublic(x509KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * RSA -> 使用私钥进行加密
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] rsaEncryptByPrivateKey(byte[] data) throws Exception {
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(RSA_PRIVATE_KEY);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * RSA -> 使用私钥进行解密
     * @param data
     * @return
     * @throws Exception
     */
    public static byte[] rsaDecryptByPrivateKey(byte[] data) throws Exception{
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(RSA_PRIVATE_KEY);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }


    /**
     * 加密解密分段处理公共方法
     */
    private static byte[] encryptAndDecrypt(byte[] data, Cipher cipher,int maxLength) throws Exception {
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > maxLength) {
                cache = cipher.doFinal(data, offSet, maxLength);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * maxLength;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }


}
